/*
 * uploads.cpp
 *
 *  Created on: 24/10/2010
 *      Author: teledhil
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "comunicacion.h"
#include "consola.h"
#include "uploads.h"
#include "variablesGlobales.h"

#define UNUSED(x) ((void)(x))

void *funcion_uploads(void *arg) {

	char mensaje[512];

	char hostname[128];
	struct hostent *hostent_info;
	struct in_addr * address;
	int c1, c2, c3, c4;
	unsigned int ip;
	int port, fd;
	off_t longitud;

	uint32_t leido_respuesta; //uint32_t es de 4 bytes
	long leido_ack, total_ack;

	int longLeido, longEscrito;
	char fragmento[BIG_BUFFER + 1] = "";
	char file_ul_path_temp[BUFFER] = "";
	char *name_file, *temp;

	bool *deboMorir;

	deboMorir = (bool *) arg;

	output_debug("Hilo_uploads: Hilo en ejecucion!");

	//Abre fichero
	if (file_ul_fd > 0) {
		output_program("Unable to offer file. Another file has been offered");
		output_debug("file_ul_fd=%d", file_ul_fd);
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}
	if ((file_ul_fd = open(file_ul_path, O_RDONLY)) < 0) {
		output_program("Unable to share file.");
		output_error("%s", strerror(errno));
		file_ul_fd = 0;
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}
	//Obtiene longitud del fichero
	if ((longitud = lseek(file_ul_fd, 0, SEEK_END)) < 0) {
		output_program("Unable to determine file size (%ld).", longitud);
		output_debug("file_ul_fd=%d", file_ul_fd);
		output_error("%s", strerror(errno));
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}
	file_ul_size = longitud;
	output_debug("Longitud fichero %d", file_ul_size);
	lseek(file_ul_fd, 0, SEEK_SET);

	//Obtiene ip:
	gethostname(hostname, sizeof hostname);
	if ((hostent_info = gethostbyname(hostname)) == NULL) {
		output_error("offer: gethostbyname");
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}
	address = (struct in_addr *) hostent_info->h_addr;
	output_debug("Mi IP: %s", inet_ntoa(*address));
	sscanf(inet_ntoa(*address), "%d.%d.%d.%d", &c1, &c2, &c3, &c4);
	ip = (unsigned int) c4 + c3 * 256 + c2 * 256 * 256 + c1 * 256 * 256 * 256;
	output_debug("The unsigned integer ip is %u", ip);

	/*Obtiene socket*/
	if ((socketUpload = crear_conexion()) < 0) {
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}

	/*Asociar el socket a un puerto*/
	if ((port = asociar(socketUpload, DCC_PORT)) < 0) {
		output_program("Unable to offer file");
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		cerrar_conexion(socketUpload);
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}

	//Envia ofrecimiento de fichero
	output_program("Sending offer of file %s to %s", file_ul_path, file_ul_nick);
	strcpy(file_ul_path_temp, file_ul_path);
	name_file = file_ul_path;
	temp = strtok(file_ul_path_temp, "/");
	while (temp) {
		name_file = temp;
		temp = strtok(NULL, "/");
	}
	sprintf(mensaje, "%s%c%s%c%c%c%s%c%s%c%s%c%u%c%d%c%ld%c%c%c", IRC_PRIVMSG,
			SPACE, file_ul_nick, SPACE, COLON, DCC_CODE, DCC, SPACE, DCC_SEND,
			SPACE, name_file, SPACE, ip, SPACE, port, SPACE, file_ul_size,
			DCC_CODE, CR, LF);
	enviar_mensaje(socketServer, mensaje, strlen(mensaje));

	/*Esperar conexion al puerto de transferencias*/
	output_debug("A la espera de cliente");
	while (((fd = esperar(socketUpload)) <= 0) && !state_powerOff
			&& !(*deboMorir)) {
		if (fd < 0) {
			output_program("Unable to offer file");
			cerrar_conexion(file_ul_fd);
			file_ul_fd = 0;
			//cerrar_conexion(socketUpload);
			output_debug("Hilo_uploads: Muero");
			return NULL;
		}
	}
	if (state_powerOff || (*deboMorir)) {
		output_debug("Hilo_uploads: Muero");
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		//cerrar_conexion(socketUpload);
		return NULL;
	}
	output_debug("Cliente conectado!");

	//Enviar fichero
	if (state_sleep > 0) {
		sleep(state_sleep);
	}
	output_program("%s is downloading file %s", file_ul_nick, file_ul_path);
	while (((longLeido = read(file_ul_fd, fragmento, 1)) > 0)
			&& !state_powerOff && !(*deboMorir)) {
		if (state_sleep > 0) {
			sleep(state_sleep);
		}
		if ((longEscrito = enviar_mensaje(fd, fragmento, longLeido)) < 0) {
			output_program("Sending file failed. Couldn't send all file");
			cerrar_conexion(file_ul_fd);
			file_ul_fd = 0;
			//cerrar_conexion(socketUpload);
			output_debug("Hilo_uploads: Muero");
			return NULL;
		}
		//Recibe ack ultimo trozo enviado
		total_ack = 0;
		while (total_ack < ((int)sizeof(uint32_t))) {
			if ((leido_ack = recibir_mensaje(fd, (char *) &leido_respuesta,
					(sizeof(uint32_t) - total_ack))) < 0) {
				output_program("Bad ackin");
				cerrar_conexion(file_ul_fd);
				file_ul_fd = 0;
				//cerrar_conexion(socketUpload);
				output_debug("Hilo_uploads: Muero");
				return NULL;
			}
			total_ack += leido_ack;
		}
		//output_debug("ack=%ld", ntohl(leido_respuesta));
		if(file_ul_size==((long)ntohl(leido_respuesta))){
			output_debug("ack del fichero completo");
			close(fd);
		}
	}
	if (longLeido < 0) {
		output_program("Sending file failed. Couldn't read file");
		cerrar_conexion(file_ul_fd);
		file_ul_fd = 0;
		//cerrar_conexion(socketUpload);
		output_debug("Hilo_uploads: Muero");
		return NULL;
	}
	output_program("%s finished the download of file %s", file_ul_nick,
			file_ul_path);

	output_debug("Hilo_uploads: Muero");

	file_ul_fd = 0;
	//cerrar_conexion(fd);
	//cerrar_conexion(socketUpload);
	return NULL;
}
